package demo.security.realm;

import demo.security.constant.SecurityConst;
import demo.rbac.entity.User;
import demo.rbac.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 自定义Realm，用于演示Shiro与Spring Boot整合
 */
public class SpringDemoRealm extends AuthorizingRealm {

  {
    /*
       设置加密算法
     */
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("md5");
    matcher.setHashIterations(1);
    this.setCredentialsMatcher(matcher);
  }

  @Autowired
  private UserService userService;

  @Override
  public void setName(String name) {
    super.setName("springDemoRealm");
  }

  /**
   * 授权：
   * 登录之后，所有调用subject.hasRole(...)或者subject.isPermitted(...)或者@RequiresPermissions(...)等注解的地方，
   *    会调用Realm的doGetAuthorizationInfo()
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //1.获取认证的用户数据
    User user = (User)principalCollection.getPrimaryPrincipal();

    //2.构造授权数据
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    // 获取用户的角色、权限
    List<String> roleNames = userService.getRolesByUserId(user.getId());
    authorizationInfo.addRoles(roleNames);
    List<String> permissionCodes = userService.getPermissionCodesByUserId(user.getId());
    authorizationInfo.addStringPermissions(permissionCodes);

    return authorizationInfo;
  }

  /**
   * 认证：
   * LoginController里的subject.login(token) --> securityManager --> 委托给Authenticator进行身份验证
   *    --> 把相应的token传入Realm，从Realm获取身份验证信息 --> Authenticator执行验证
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    //1.获取登录的upToken
    UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;

    //2.获取输入的用户名密码
    String username = upToken.getUsername();

    //3.数据库查询用户
    User user = userService.findByName(username);

    if (user == null) {
      return null;
    }

    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
    // 如果密码有加盐，需要多设置一下setCredentialsSalt
    authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(SecurityConst.SALT));

    return authenticationInfo;
  }
}
